package com.scut.filedownloader;

import android.Manifest;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.scut.filedownloader.core.Downloader;
import com.scut.filedownloader.core.cocurrent.SharedThreadPool;
import com.scut.filedownloader.core.interact.AbsDownloadMonitor;
import com.scut.filedownloader.core.interact.ProgressMonitor;
import com.scut.filedownloader.core.recorder.DownloadEntryRecorder;
import com.scut.filedownloader.core.recorder.DownloadRecorder;
import com.scut.filedownloader.ui.adapter.RecyclerViewLinearAdapter;
import com.scut.filedownloader.ui.protocol.TaskDialogCallBack;
import com.scut.filedownloader.util.FDFormatter;
import com.scut.filedownloader.util.MessageBuilder;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class MainActivity extends AppCompatActivity {

    //UI Outlet
    private RecyclerView recyclerView;
    private Menu menu;
    private ProgressBar widget_loadingCircle;
    private Controller controller=null;

    //permission list
    private static final String[] PERMISSIONS={
            Manifest.permission.WRITE_EXTERNAL_STORAGE
    };
    private static final int REQUEST_PERMISSION_CODE=1;
    Downloader downloader;

    Handler mHandler=new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what){
                case UIMessageCode.SET_RV_ADAPTER:
                    if(recyclerView!=null){
                        recyclerView.setAdapter((RecyclerView.Adapter) msg.obj);
                        recyclerView.getAdapter().notifyDataSetChanged();

                        LinearLayoutManager linearLayoutManager=new LinearLayoutManager(MainActivity.this,RecyclerView.VERTICAL,false);
                        recyclerView.setLayoutManager(linearLayoutManager);
                        recyclerView.setItemAnimator(new DefaultItemAnimator());
                    }
                    break;
                case UIMessageCode.UI_UPDATE_TASK:
                    Runnable work= (Runnable) msg.obj;
                    if(work!=null){
                        work.run();
                    }
                    break;
                case UIMessageCode.MAKE_TOAST:
                    String text= (String) msg.obj;
                    if(text==null){
                        text="";
                    }
                    Toast.makeText(MainActivity.this,text,Toast.LENGTH_SHORT);
                    break;
                case UIMessageCode.HIDE_LOADING_CIRCLE:
                    if(widget_loadingCircle!=null){
                        widget_loadingCircle.setVisibility(View.GONE);
                    }
                default:
                    super.handleMessage(msg);
                    break;
            }
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //set up ui
        setContentView(R.layout.main);
        recyclerView=findViewById(R.id.container); widget_loadingCircle=findViewById(R.id.widget_progress_circle);

        FileDownloader app=FileDownloader.getInstance();
        Log.d("MainActivity", "onCreate: "+getPackageResourcePath());
       // Log.d("MainActivity","app="+(app==null));
        AsyncLoadingTask loadingTask=new AsyncLoadingTask();
        SharedThreadPool.getInstance().executeTask(loadingTask, SharedThreadPool.PRIORITY.LOW);

        final int CHECK_RESULT= getPackageManager().checkPermission(PERMISSIONS[0],getPackageName());
        if(CHECK_RESULT!= PackageManager.PERMISSION_GRANTED){
            //request permission here
            requestPermissions(PERMISSIONS,REQUEST_PERMISSION_CODE);
        }

    }


    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if(requestCode==REQUEST_PERMISSION_CODE){
            if(grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
                makeToast("Permission granted");
            }
            else {
                makeToast("Permission denied");
            }
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        this.menu=menu;
        getMenuInflater().inflate(R.menu.menu,menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        boolean consume=false;
        switch (item.getItemId()){
            case R.id.menu_add_task:
                if(controller==null)
                    consume=false;
                else {
                    controller.createAddTaskDialog();
                    consume = true;
                }
                break;
            default:
                break;
        }
        return consume;
    }

    private void makeToast(String toast){
        mHandler.sendMessage(
                MessageBuilder.obtain(UIMessageCode.MAKE_TOAST,toast)
        );
    }


    /**
     * 加载耗时的异步任务，防止主线程卡顿
     */
    private class AsyncLoadingTask implements Runnable{

        @Override
        public void run() {


            controller=new Controller();
            controller.init_and_load_task_list();
            mHandler.sendEmptyMessage(UIMessageCode.HIDE_LOADING_CIRCLE);
        }
    }

    /**
     * 一个内嵌在MainActivity的简单控制器，用于处理下载条目的初始载入,以及处理新下载任务的请求, 因此实现了了TaskDialogCallBack的接口
     */
    private class Controller implements TaskDialogCallBack {

        RecyclerViewLinearAdapter adapter=null;
        DownloadEntryRecorder entryRecorder=null;

        Controller(){
            downloader=Downloader.getInstance(getApplicationContext());

        }

        private void init_and_load_task_list(){
            entryRecorder=FileDownloader.getInstance().getRecorderFactory().getDownloadEntryRecorder();


            //载入任务id
            Map<String,Integer> map=entryRecorder.getAllEntries();

            //载入下载信息到内存
            int entry_count=map.size();
            Collection<Integer> collectionId= map.values();
            Iterator<Integer> iterator=collectionId.iterator();
            List<RecyclerViewLinearAdapter.ItemData> listOfItems=new ArrayList<>(entry_count);
            while(iterator.hasNext()){
                DownloadRecorder recorder=entryRecorder.getDownloadRecorder(iterator.next());
                if(recorder!=null&&!recorder.isEmptyEntry()){
                    TaskItem item=new TaskItem();
                    item.title=recorder.getFileName();
                    item.url=recorder.getUrl();
                    item.content_length=recorder.getFileSize(); //isEmptyEntry排除为0
                    item.full_pathname=recorder.getPathName();

                    final int blockCount= (int) recorder.getBlockCount();
                    final long blockSize=recorder.getBlockSize();
                    for(int i=0;i<=blockCount;i++){
                        final long valid_data_length_in_block_i=recorder.getBlockInfo(i);
                        if(valid_data_length_in_block_i>-1){
                            item.tracker.put(i,valid_data_length_in_block_i);
                        }
                    }

                    item.progress =item.reportValueByTracker();

                    item.setState( recorder.getFinishedState()? ProgressMonitor.ProgressState.COMPLETED: ProgressMonitor.ProgressState.READY);
                    listOfItems.add(item);
                }
            }

            adapter=new RecyclerViewLinearAdapter(listOfItems);
            mHandler.sendMessage(
                MessageBuilder.obtain(UIMessageCode.SET_RV_ADAPTER,adapter)
            );
        }

        private void createAddTaskDialog(){
            //AlertDialog dialog;
            AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this);
            View dialog=getLayoutInflater().inflate(R.layout.add_task_form,null);
            builder.setView(dialog);

            final EditText editText_src=dialog.findViewById(R.id.edit_url);
            final EditText editText_filename=dialog.findViewById(R.id.edit_filename);

            builder.setPositiveButton("CONFIRM", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    onConfirm(editText_src.getText().toString(),editText_filename.getText().toString());
                    dialog.dismiss();
                }
            });

            builder.setNegativeButton("CANCEL",null);
            builder.create().show();

        }

        @Override
        public void onCancel(String text1, String text2) {
            //stub
        }

        @Override
        public void onConfirm(String text1, String text2) {
            //be ready to execute download task here
            //check input first
            if(text1!=null&&!text1.isEmpty() &&text2!=null &&!text2.isEmpty()){
                TaskItem item=new TaskItem(text1,text2);
                item.setState(ProgressMonitor.ProgressState.GOING);
                //add to list
                adapter.addItem(item);
                downloader.download(text1,text2,item);
            }
            else{
                //toast user
                makeToast("请输入有效数据");
            }
        }

    }

    /**
     * 每一个任务项都是循环视图的数据来源,同时也是任务监视器,这里采用聚合模式, 使得TaskItem类具有多方能力
     */
    private class TaskItem extends AbsDownloadMonitor
                implements RecyclerViewLinearAdapter.ItemData {

            String full_pathname;
            String title = "";
            String url = "";
            long content_length = 0L;
            long progress = 0L;
            RecyclerViewLinearAdapter.ViewHolderProxy viewHolder;



            TaskItem() {
                super();
            }

            TaskItem(String url, String title) {
                this.url=url;
                this.title=title;
            }

            @Override
            public String getTitle() {
                return title;
            }

            @Override
            public String getURL() {
                return url;
            }

            @Override
            public String getProgressDesc() {
                StringBuilder builder = new StringBuilder();
                total_progress_desc =  (total_progress_desc == null ||total_progress_desc.isEmpty())? FDFormatter.getSuitableFileSizeString(content_length) : total_progress_desc;
                builder.append(FDFormatter.getSuitableFileSizeString(progress) + "/" + total_progress_desc);
                builder.append(" ").append(state.name());
                return builder.toString();
            }

            public void setState(ProgressState state){
                this.state=state;
            }

            @Override
            public RecyclerViewLinearAdapter.ViewHolderProxy getBoundViewHolder() {
                return viewHolder;
            }

            @Override
            public void bindViewHolder(RecyclerViewLinearAdapter.ViewHolderProxy holder,  int position) {
                viewHolder = holder;


                viewHolder.tv_title.setText(title);
                viewHolder.tv_url.setText(url);
                viewHolder.tv_progress_desc.setText(getProgressDesc());
                viewHolder.progressBar.setMax(100);

                if (content_length != 0) {
                    final int indicate_progress = (int) (((float) progress / content_length) * 100);
                    viewHolder.progressBar.setProgress(indicate_progress, true);
                } else {
                    viewHolder.progressBar.setProgress(0);
                }

                if (state == ProgressMonitor.ProgressState.COMPLETED) {
                    viewHolder.firstBtn.setText("DONE");
                } else if(state== ProgressState.GOING){
                    viewHolder.firstBtn.setText("PAUSE");
                }
                else if(state==ProgressState.READY){
                    viewHolder.firstBtn.setText("RESUME");
                }

                //绑定按键事件
                viewHolder.secondBtn.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if(state==ProgressState.GOING||state==ProgressState.PAUSED){
                            cancel();
                        }
                        //移除表项
                        controller.adapter.removeItem(viewHolder.getAdapterPosition());
                        //移出entry
                        controller.entryRecorder.deleteRecordEntry(title);
                    }
                });

                viewHolder.firstBtn.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if(state==ProgressState.COMPLETED){
                            controller.adapter.removeItem(viewHolder.getAdapterPosition());
                            return;
                        }
                        if (state==ProgressState.GOING||state==ProgressState.PAUSED) { //任务进行中
                            if (interruptSignal()) {
                                //当前未中断状态，转向继续状态
                                //变更提示
                                ((Button) v).setText("PAUSE");
                            } else {
                                //当前非中断
                                ((Button) v).setText("RESUME");
                            }
                            interrupt();
                        } else if(state==ProgressState.READY){ //READY状态开始执行任务
                            //唤起下载任务
                            ((Button) v).setText("PAUSE");
                            invokeDownloadTask();
                        }
                    }
                });

            }

            private void invokeDownloadTask() {
                downloader.download(url, title, this);
            }

            HashMap<Integer,Long> tracker=new HashMap<>();
            long startTime;
            long lastPauseTime;

            String progress_desc="",total_progress_desc="",speed_desc="",error_desc=""; //SpannableString可能更方便
            @Override
            public void onProgress(int key, long value) {
                if(key==0){
                    content_length=value;
                    total_progress_desc=FDFormatter.getSuitableFileSizeString(value);
                }
                else{

                    tracker.put(key,value);
                    long now=SystemClock.elapsedRealtime();
                    //Log.d("time elapsed", "onProgress: "+(now-startTime));
                    progress=reportValueByTracker();
                    speed_desc=calculateSpeed(now,startTime,progress);
                    progress_desc=FDFormatter.getSuitableFileSizeString(progress);

                    updateProgressDesc(constructProgressDesc(
                            total_progress_desc,progress_desc,speed_desc,state.name(),error_desc
                    ));

                    updateProgress(
                            calculateProgressi(progress,content_length)
                    );
                }
            }

            @Override
            public void receiveMessage(Downloader.MessageCode messageCode, String msg) {
                if(messageCode.name().startsWith("ERR")){
                    makeToast("task: "+"ERROR: "+messageCode.name()+": "+msg);
                    error_desc=msg;
                }
                else{
                    switch (messageCode){
                        case NOTICE_RESERVING_SPACE:
                            updateProgressDesc("reserving space now");
                            break;
                    }
                }
            }

            @Override
            public void receiveMessage(Downloader.MessageCode messageCode, int varInt) {
                switch (messageCode){
                    case NOTICE_ERROR_TASK_COUNT:
                        makeToast("Error task count: "+varInt);
                        break;
                    case NOTICE_HTTP_RECONNECT:
                        makeToast("reconnect times: "+varInt);
                        break;
                }
            }

            private String constructProgressDesc(String total,String downloaded,String speed,String state,String msg){
                return downloaded.concat("/").concat(total).concat(" ").concat(speed).concat(" ").concat(state).concat(" "+msg);
            }

            private class UpdateProgressDescWork implements  Runnable {
                String desc="";
                @Override
                public void run() {
                    if(viewHolder!=null) {
                        viewHolder.tv_progress_desc.setText(desc);
                    }
                }

                Runnable setDesc(final String desc){
                    this.desc=desc;
                    return this;
                }
            };

            UpdateProgressDescWork updateProgressDescWork=new UpdateProgressDescWork();
            UpdateProgressWork updateProgressWork=new UpdateProgressWork();
            UpdateButtonTextWork updateButtonTextWork=new UpdateButtonTextWork();

            private class UpdateProgressWork implements Runnable {
                int progress=0;
                @Override
                public void run() {
                    if(viewHolder!=null) {
                        viewHolder.progressBar.setProgress(progress, true);
                    }
                }

                Runnable setProgress(final int progress){
                    this.progress=progress;
                    return this;
                }
            }

            private class UpdateButtonTextWork implements Runnable{
                Button btn;
                CharSequence cs;
                @Override
                public void run() {
                    btn.setText(cs);
                }

                Runnable setBtnText(final Button btn,final String str){
                    this.btn=btn;
                    this.cs=str;
                    return this;
                }
            }

            private void updateProgressDesc(final String desc){
                mHandler.sendMessage(
                        MessageBuilder.obtain(UIMessageCode.UI_UPDATE_TASK, updateProgressDescWork.setDesc(desc))
                );
            }

            private void updateProgress(final int progress){
                mHandler.sendMessage(
                        MessageBuilder.obtain(UIMessageCode.UI_UPDATE_TASK, updateProgressWork.setProgress(progress))
                );
            }

            private void updateBtnText(final Button btn,String text){
                mHandler.sendMessage(
                        MessageBuilder.obtain(UIMessageCode.UI_UPDATE_TASK,updateButtonTextWork.setBtnText(btn,text))
                );
            }
            @Override
            public void onStart() {
                super.onStart();
                startTime= SystemClock.elapsedRealtime();
                startTime=startTime<0?0:startTime;
            }

            @Override
            public void onStop(ProgressState state) {
                super.onStop(state);
                //viewHolder.firstBtn.setText("CONTINUE");
                updateProgressDesc(constructProgressDesc(total_progress_desc,progress_desc,"",state.name(),error_desc));
                updateBtnText(viewHolder.firstBtn,"RESUME");
            }

            @Override
            public void onFinished() {
                super.onFinished();
                updateBtnText(viewHolder.firstBtn,"DONE");
                updateProgressDesc(constructProgressDesc(total_progress_desc,progress_desc,"",state.name(),error_desc));
            }

            @Override
            public void onPause() {
                lastPauseTime=SystemClock.elapsedRealtime();
                state=ProgressState.PAUSED;
                updateProgressDesc(constructProgressDesc(total_progress_desc,progress_desc,"",state.name(),error_desc));
            }

            @Override
            public void onResume() {
                startTime+=lastPauseTime-SystemClock.elapsedRealtime();
                state=ProgressState.GOING;
                updateProgressDesc(constructProgressDesc(total_progress_desc,progress_desc,"0B/s",state.name(),error_desc));
            }


            long reportValueByTracker(){
                long sum=0L;
                Collection<Long> values=tracker.values();
                for (long l: values) {
                    sum+=l;
                }
                return sum;
            }

            private String calculateSpeed(long now,long startTime, long byteOfCopied){
                long duration=now-startTime;  //ms
                String speed_desc;
                if(duration!=0) {
                    long mean = byteOfCopied / duration; //  byte/ms
                    mean*=1000; //byte/s
                    speed_desc= FDFormatter.getSuitableFileSizeString(mean);
                }
                else{
                    speed_desc="calculating";
                }
                return speed_desc+"/s";
            }

            private int calculateProgressi(long byteOfCopied,long numberOfBytesNeededToCopy){

                int progress= numberOfBytesNeededToCopy!=0?
                (int) (
                        (((float)byteOfCopied)/numberOfBytesNeededToCopy)
                                *100
                ):
                0
                ;
                return progress;
            }

        }


//    private void testUI_prepare(){
//        listOfItem=new ArrayList<>();
//        listOfItem.add(new RecyclerViewLinearAdapter.ItemData() {
//
//            RecyclerViewLinearAdapter.ViewHolderProxy viewHolder;
//            int position;
//            @Override
//            public String getTitle() {
//                return "ubuntu, this is title";
//            }
//
//            @Override
//            public RecyclerViewLinearAdapter.ViewHolderProxy getBoundViewHolder() {
//                return viewHolder;
//            }
//
//            @Override
//            public String getURL() {
//                return "http://this is a download link ";
//            }
//
//            @Override
//            public String getProgressDesc() {
//                return "2.30GB/2.5GB 100kb/s Downloading";
//            }
//
//            @Override
//            public void bindViewHolder(RecyclerViewLinearAdapter.ViewHolderProxy holder, int position) {
//                this.viewHolder=holder;
//                this.position=position;
//
//                viewHolder.firstBtn.setText("PAUSE");
//                viewHolder.tv_progress_url.setText(getProgressDesc());
//                viewHolder.tv_title.setText(getTitle());
//                viewHolder.tv_url.setText(getURL());
//                viewHolder.progressBar.setMax(100);
//                viewHolder.progressBar.setProgress(0);
//            }
//        });
//
//        itemData=listOfItem.get(0);
//        adapter=new RecyclerViewLinearAdapter(listOfItem);
//        mHandler.sendMessage(
//                MessageBuilder.obtain(UIMessageCode.SET_RV_ADAPTER,adapter)
//        );
//    }
//
//    private void testUI_changing(){
//        final ProgressBar progressBar=listOfItem.get(0).getBoundViewHolder().progressBar;
//        adapter.addItem(itemData);
//        mHandler.sendMessage(MessageBuilder.obtain(UIMessageCode.UI_UPDATE_TASK, new Runnable() {
//            @Override
//            public void run() {
//                progressBar.setProgress(progressBar.getProgress()+2,true);
//            }
//        }));
//    }

    final static class UIMessageCode{
        final static int SET_RV_ADAPTER=0;
        final static int UI_UPDATE_TASK=1; //obj:Runnable
        final static int MAKE_TOAST=2;
        final static int HIDE_LOADING_CIRCLE =3;
    }


}
